/*
 * @Description: 类的修饰符
 * @Autor: HuiSir<273250950@qq.com>
 * @Date: 2021-04-30 13:51:33
 * @LastEditTime: 2021-04-30 18:44:42
 */

/**
 * 修饰符用来描述类中成员的可访问性
 * 主要包含：public,private,protected,readonly,static,get,set
 * get和set官方说法是类中的存取器（用于劫持读写），编译为js后是使用Object.defineProperty方法
 * 注意：在ES6中的提案 #号开头的私有属性并不支持
 */


/**
 * public 公共属性/方法，在当前类、子类、外部实例都可以访问。默认状态都是公共的，所以public可以省略
 * private 私有属性/方法，只能在当前类中使用。在子类和外部均不可使用
 * protected 受保护的属性/方法，只能在当前类及其派生类中访问
 * readonly 只读属性，初始化赋值之后就不能修改（除了初始化，任何地方都不能改），和const作用类似
 * static 静态属性/方法，类可以直接调用无需实例化，另外，构造函数是不能用static修饰的
 * get 监听到读取方法，可以做一些事,读的时候无需执行此方法，可以直接读（无需执行，可以看做一个属性）和vue计算属性差不多
 * set 监听到写入方法，可以做一些事，get和set关键字只能用于在方法前面
 */

/**
 * get 和 set 访问器必须属于同一类型（get的返回值类型和set的参数类型一致）。
 * set 存储器必须正好具有一个参数。（因为要使用=号赋值，只能赋值一个值）
 * readonly 修饰符有特殊的特性：如果在构造函数的参数中使用readonly，则会默认在类中声明一个属性，就无需再次申明，如下User类中的id参数
 */

(function () {
    class User {
        public name: string
        private money: number // 私有屬性
        protected age: number // 受保护的属性，只能在当前类及其派生类中访问
        readonly sex: string  // 只读属性，不可修改
        static state: number = 1    // 静态属性

        public constructor(name: string, money: number, age: number, sex: string = '男', readonly id: string | number = Date.now()) {
            this.name = name
            this.money = money
            this.age = age
            this.sex = sex
        }

        // 静态方法
        static getState() {
            return User.state
        }

        // 读取方法
        get myMoney(): string {
            // return this.money + "元" // 报错：不能将类型“string”分配给类型“number”
            return this.money + "元"
        }

        // 写入方法
        set myMoney(money: string) {
            // this.money = money // 报错：不能将类型“string”分配给类型“number”
            this.money = Number(money)
        }

        // set setSex(sex:string) {
        //     this.sex = sex  //报错：无法分配到 "sex" ，因为它是只读属性。
        // }
    }

    class Admin extends User {
        // get getMoney() {
        //     return this.money // 报错：属性“money”为私有属性，只能在类“User”中访问。
        // }

        get nextAge() {
            console.log("执行了get");
            return this.age + 1 // 受保护的属性在子类中可以读写
        }

        set nextAge(age) {  // set 方法中的参数age 实际在调用时候等同于`this.nextAge = 11`中的11
            this.age = age
            console.log("执行了set") // 受保护的属性在子类中可以读写
        }

        getSex(): string {
            return this.sex
        }

        // setSex(sex) {
        //     this.sex = sex //报错：无法分配到 "sex" ，因为它是只读属性。
        // }
    }

    let admin = new Admin("小明", 100, 15)

    console.log(admin.nextAge)  // 执行了get 16

    admin.nextAge = 10   // 执行了set

    console.log(admin.nextAge) // 执行了get 11

    // console.log(admin.age) // 报错：属性“age”受保护，只能在类“User”及其子类中访问。

    // console.log(admin.money) // 报错：属性“money”为私有属性，只能在类“User”中访问。

    console.log(admin.myMoney) // 100元
    admin.myMoney = "1000"
    console.log(admin.myMoney) // 1000元

    console.log(admin.getSex()) // 男
    // admin.sex = "女"    //报错：无法分配到 "sex" ，因为它是只读属性。

    console.log(admin.id);  // 1619775257212

    console.log("=================================");

    console.log(User.state) // 1
    User.state = 2
    console.log(User.getState()) // 2


    console.log("=================================");


    /**
     * geter和seter练习
     */

    class Persion {
        firstName: string
        lastName: string

        constructor(firstName: string, lastName: string) {
            this.firstName = firstName
            this.lastName = lastName
        }

        // 访问器
        get fullName() {
            const { firstName, lastName } = this
            return firstName + "-" + lastName
        }

        // 存储器
        set fullName(val) {
            console.log("触发了存储器")
            let [firstName, lastName] = val.split("-")
            this.firstName = firstName
            this.lastName = lastName
        }
    }

    const p1 = new Persion("东方", "不败")
    console.log(p1) //{firstName: '东方', lastName: '不败'}
    console.log(p1.fullName)    // 东方-不败 ,这个属性会被创建到prototype中
    p1.fullName = "令狐-冲"     // 触发了存储器
    console.log(p1.fullName)   // 令狐-冲
})()